/*
	Project: Socket_lib
	File: app_server_main.cpp
	Purpose: FTP server to store files received from clients and send requested files back
	Authors: Brooke Thrower and Jeramie Hallyburton
	Date: April 6, 2011
*/

#define _SCL_SECURE_NO_WARNINGS //added to use std::copy() without warnings
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
#include "../include/socket.hpp"
#include "../include/helper.hpp"
#include <boost\thread\thread.hpp>
#include <sys/stat.h>
#include <exception>
#include <vector>

#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;

//location for server files
fs::path storagePath(fs::initial_path().string() + "\\ServerFiles");

void ClientThread( Socket* clientSocket ) {
	bool blnNeedFileInfo = true;
	string origFilename;
	unsigned long origFilesize;
	fs::path recvFilePath;
	unsigned long recvFileSize = 0;
	ofstream outfile;
	bool blnGettingFile = true;

	fs::path sendFilePath;

	char sendBuf[32] = "Welcome! You are now connected.";
	//send welcome message to client
	clientSocket->Send( sendBuf );
		
	//wait for messages from client
	while( true )
	{
		//receive message from client
		char recvBuf[1024];
		clientSocket->Receive( recvBuf, 1024 );

		if( strcmp( recvBuf, "Sending" ) == 0 )
		{
			//send message back to client
			char sendbuf[32] = "OK";
			int bytesSent = clientSocket->Send( sendbuf );
			cout << "Sent " << bytesSent << " bytes"<<endl;
			blnGettingFile = true;

		}
		else if( strcmp( recvBuf, "Receiving" ) == 0 )
		{
			//send message back to client
			char sendbuf[32] = "OK";
			int bytesSent = clientSocket->Send( sendbuf );
			cout << "Sent " << bytesSent << " bytes"<<endl;
			blnGettingFile = false;

		}
		else if( strcmp( recvBuf, "End" ) == 0 )
		{
			cout<<"File Received."<<endl;
			//check if files match
			outfile.close();

			blnNeedFileInfo = true;

			recvFileSize = (unsigned long)fs::file_size(recvFilePath);

			//send message back to client
			if( recvFileSize == origFilesize )
			{
				char sendbuf[32] = "Done";
				int bytesSent = clientSocket->Send( sendbuf );
				cout << "Sent " << bytesSent << " bytes"<<endl;;
			}
			else
			{
				//send message back to client
				char sendbuf[32] = "Failed";
				int bytesSent = clientSocket->Send( sendbuf );
				cout << "Sent " << bytesSent << " bytes"<<endl;;
				cout << "Error: File not transferred properly"<<endl;
			}
		}
		else if( strcmp( recvBuf, "BeginSend" ) == 0 )
		{
			cout<< "Begin sending."<<cout;
			//Begin Sending File
			ifstream sendFile;
			sendFile.open( sendFilePath.string().c_str(), std::ios::in | std::ios::binary );

			//file will be broken into chunks of 1024 bytes
			while (!sendFile.eof())
			{
				char   buf[1024];
				sendFile.read ( buf, sizeof(buf) - 1 );
				int bytesRead = (int)sendFile.gcount();
				buf[bytesRead] = '\0';
				clientSocket->Send( buf );

				//recieve ack from client
				clientSocket->Receive( recvBuf, 32 );
			}
			sendFile.close();
			char sendbuf[32] = "End";
			clientSocket->Send( sendbuf );
			blnGettingFile = true;	
			cout << "Sending complete."<<endl;
		}
		else if( strcmp( recvBuf, "Quit" ) == 0 ) {
			break;
		}
		else
		{
			//get file information
			if( blnNeedFileInfo)
			{
				//set file info
				istringstream iss(recvBuf);
				iss >> origFilename;
				iss >> origFilesize;
				blnNeedFileInfo = false;

				recvFilePath = fs::path(storagePath.string() + "\\" + origFilename);
				if( blnGettingFile ) // server receiving file
				{
					cout<<"Begin Receiveing file."<<cout;
					outfile.open( recvFilePath.string(), std::ios::out | std::ios::binary );
					//send message back to client
					char sendbuf[32] = "OK";
					clientSocket->Send( sendbuf );
				}
				else
				{
					//send file size back to client
					sendFilePath = fs::path(storagePath.string() + "\\" + origFilename);

					if(fs::exists(sendFilePath)) 
					{
						__int64 size = fs::file_size(sendFilePath);
						
						stringstream out;
						string filesize;
						out << size;
						filesize = out.str();

						//send message back to client with filesize
						char sendbuf[32];
						filesize.copy( sendbuf, filesize.length(), 0 );
						clientSocket->Send( sendbuf );		
					}
					else
					{
						//send message back to client
						char sendbuf[32] = "Error";
						clientSocket->Send( sendbuf );	
					}
				}
			}
			else
			{
				stringstream ss(recvBuf);
				outfile.write( ss.str().c_str(), ss.str().length() );
				//send message back to client
				char sendbuf[32] = "OK";
				clientSocket->Send( sendbuf );
			}
		}
	}
}


void ServerThread( Socket* listeningSocket ) {
	cout << "Server thread started" << endl;

	//bind to the interface for listening
	if( !listeningSocket->Bind( "127.0.0.1", 8478 ) )
		cout << "There was a problem binding to socket. " << endl;

	//listen for connection
	if( !listeningSocket->Listen() ) 
		cout << "There was an error listening." << endl;

	//check if server's file location exists, if not create it
	if( !fs::exists(storagePath) )
	{
		fs::create_directory(storagePath);
	}

	//waiting for connection
	Socket clientSocket( AF_INET, IPPROTO_TCP );
	while( true ) {
		if( listeningSocket->Accept( clientSocket ) ) 
		{
			cout << "New client connection made" << endl;

			//spawn new client thread
			boost::thread clientThread( boost::ref( boost::bind( ClientThread, &clientSocket ) ) );
		}
	}
}

int main() 
{
	//start main server thread
	Socket serverSocket( AF_INET, IPPROTO_TCP );
	boost::thread serverThread( boost::bind( ServerThread, &serverSocket ) );
	serverThread.join();
}